package it.polimi.meteocal_pappalardo_sacchi.bean;

import it.polimi.meteocal_pappalardo_sacchi.entity.Event;
import it.polimi.meteocal_pappalardo_sacchi.entity.WeatherForecast;

import javax.annotation.PostConstruct;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;

/**
 * Created by Alessandro on 18/01/15.
 */
@Named
@ViewScoped
public class EditEventBean extends EventBean implements Serializable {

    private Event oldEvent;
    private Long eventId;

    public void editEventInit() {
        // Do here your thing which should run on initial (GET) request only. In questo modo, dato che prerenderview viene chiamato ad ogni aggiornamento della view, update inclusi, non viene tutto rinizializzato
        if (!FacesContext.getCurrentInstance().isPostback()) {
            //controllo di sicurezza che il creatore dell'evento stia accedendo alla pagina
            if (editAllowanceCheck(eventId)) {
                setEvent(eventManager.find(eventId));
                this.setOldEvent(eventManager.find(eventId));
                setAlreadyInvitedUserList(eventManager.findAlreadyInvitedUserList(getEvent()));
            } else {
                setEvent(new Event());
                this.setOldEvent(new Event());
            }
        }

    }

    //passo eventId in modo da poter chiamare il metodo anche da fuori il bean, e.g. da jsf
    public boolean editAllowanceCheck(Long eventId){
        java.sql.Date today = java.sql.Date.valueOf(LocalDate.now(ZoneId.of("Europe/Paris")));
        java.sql.Time now = java.sql.Time.valueOf(LocalTime.now(ZoneId.of("Europe/Paris")));
        Event event = eventManager.find(eventId);

        if (userBean.getUser().equals(event.getCreator()) &&
                (event.getStartingDate().after(today) ||
                        (event.getStartingDate().equals(today) && event.getStartingTime().after(now)))) {
            return true;
        }
        return false;
    }

    public String modify(){

        if(!editAllowanceCheck(eventId)) {
            return "/user/calendar?faces-redirect=true";
        }

        if (!checkAll()){
            return null;
        }
        //aggiungo gli eventuali nuovi invitati
        if (!getInvitedUserCalendarList().isEmpty()) {
            getEvent().getParticipantCalendarList().addAll(getInvitedUserCalendarList());
        }

        //se l'evento è all'aperto e ho cambiato data di inizio/fine, scarico le nuove previsioni
        if (getEvent().isOutdoor() && (!oldEvent.getStartingDate().equals(getEvent().getStartingDate()) || !oldEvent.getEndingDate().equals(getEvent().getEndingDate()))) {
            forecastBean.retrieveForecastForEvent(getEvent());
        }

        //persisto entità modificata, incluse le eventuali previsioni
        eventManager.edit(getEvent());

        //invio inviti
        notificationManager.sendInvitationToUserList(getEvent(), getInvitedUserListToNotify());

        //invio notifica di modifica agli invitati non nuovi
        notificationManager.sendModificationNotification(getAlreadyInvitedUserList(), getEvent(), oldEvent, getModifiedFields());

        //attenzione che l'invio delle eventuali notifiche deve essere fatto dopo l'edit event per avere il corretto elenco di invitati dal db
        weatherNotification();

        return "/user/calendar?faces-redirect=true";
}

    private void weatherNotification() {
        if (notificationManager.checkIfModifiedEventNeedsImmediateBadWeatherNotification(oldEvent, getEvent())) {
            for(WeatherForecast w : getEvent().getWeatherForecastList()) {
                if(weatherManager.checkBadCondition(getEvent().getWeatherCondition(), w.getWeatherCondition())) {
                    notificationManager.sendBadWeatherNotification(getEvent());
                    break;
                }
            }
        }
    }

    public String delete() {

        if(!editAllowanceCheck(eventId)) {
            return "/user/calendar?faces-redirect=true";
        }

        notificationManager.sendDeletionNotification(getEvent());
        eventManager.remove(getEvent());
        return "/user/calendar?faces-redirect=true";
    }

    private String getModifiedFields() {
        //NB poi tutte queste belle stringhe devono diventare costanti da dichiarare all'inizio della classe.
        //CleanCode says!
        //Mi sembra cosa buona e giusta!
        String message = "";

        if (!getEvent().getTitle().equals(oldEvent.getTitle())) {
            message += "The new title is " + getEvent().getTitle() + ". \n";
        }
        if (!getEvent().getDescription().equals(oldEvent.getDescription())) {
            message += "The new description is " + getEvent().getDescription() + ". \n";
        }
        if (!getEvent().getStartingTime().equals(oldEvent.getStartingTime())) {
            message += "The new starting time is " + getEvent().getStartingTime() + ". \n";
        }
        if (!getEvent().getStartingDate().equals(oldEvent.getStartingDate())) {
            message += "The new starting date is " + getEvent().getStartingDate() + ". \n";
        }
        if (!getEvent().getEndingTime().equals(oldEvent.getEndingTime())) {
            message += "The new ending time is " + getEvent().getEndingTime() + ". \n";
        }
        if (!getEvent().getEndingDate().equals(oldEvent.getEndingDate())) {
            message += "The new ending date is " + getEvent().getEndingDate() + ". \n";
        }
        if (!getEvent().getAddressWithoutCity().equals(oldEvent.getAddressWithoutCity())) {
            message += "The new address is " + getEvent().getAddressWithoutCity() + ". \n";
        }

        if (!getEvent().getCity().equals(oldEvent.getCity())) {
            message += "The new city is " + getEvent().getCity() + ". \n";
        }

        return message;
    }

    public Event getOldEvent() {
        return oldEvent;
    }

    public void setOldEvent(Event event) {
        this.oldEvent = event;
    }

    public Long getEventId() {
        return eventId;
    }

    public void setEventId(Long eventId) {
        this.eventId = eventId;
    }
}
